package org.googlecode.hltw.single.simulate;

import java.math.BigDecimal;

import org.googlecode.hltw.single.config.server.SingleAppServerNature;
import org.googlecode.hltw.single.result.SingleAppVisitResult;
import org.googlecode.hltw.single.simulate.listener.SasConsolePrintListener;
import org.googlecode.hltw.single.simulate.listener.SingleAppSimulateEventListener;
import org.googlecode.hltw.util.lang.Constants;

/**
 * Simulate the process that a single app is visited by a single batch of
 * concurrent visitors, that is, N clients visit the server at the same moment
 * 
 * @author chenjianjx
 * 
 */
public class SingleAppVisitSimulator {
	private SingleAppSimulateEventListener eventListener;

	public void setEventListener(SingleAppSimulateEventListener eventListener) {
		this.eventListener = eventListener;
	}

	public SingleAppVisitResult simulate(SingleAppServerNature serverNature, int clientConcurrency) {

		if (eventListener == null) {
			eventListener = new SasConsolePrintListener();
			System.out.println("No event listener is specified. The console print listener will be used.");
		}

		// the number of threads the server will use to serve
		int servingThread = Math.min(clientConcurrency, serverNature.getMaxThreadExtreme());
		eventListener.start(clientConcurrency, servingThread);

		BigDecimal waitMilisSum = BigDecimal.ZERO;
		BigDecimal responseMilisSum = BigDecimal.ZERO;
		BigDecimal overallMilis = Constants.NA_DEC;

		BigDecimal beginMilisIncluded = BigDecimal.ZERO;

		for (int index = 1; index <= clientConcurrency; index++) {

			// time used to handle a single request
			BigDecimal rt = serverNature.getResponseMilisPerRequest(servingThread);
			BigDecimal endMilisExcluded = beginMilisIncluded.add(rt);
			responseMilisSum = responseMilisSum.add(rt);

			int subBatchIndex = (index - 1) / servingThread + 1;
			eventListener.oneRequestDone(index, subBatchIndex, beginMilisIncluded, endMilisExcluded);

			waitMilisSum = waitMilisSum.add(endMilisExcluded);

			if (index == clientConcurrency) {
				overallMilis = endMilisExcluded;
			}

			if (index % servingThread == 0) {
				// a sub-batch of requests have been handled, now it's turn for
				// the next sub-batch
				beginMilisIncluded = endMilisExcluded;
			}

		}

		eventListener.end();

		SingleAppVisitResult result = new SingleAppVisitResult();
		result.setWaitResponseMilisSum(waitMilisSum);
		result.setOverallMilis(overallMilis);
		result.setRequestCount(clientConcurrency);
		result.setResponseMilisSum(responseMilisSum);

		return result;

	}

}
